<?php defined('SYSPATH') or die('Access error!');

class Core_Image_GD extends Image {

	protected static $_bundled;

	public static function check()
	{
		if ( ! function_exists('gd_info'))
		{
			throw new Core_Exception('GD是不是没有安装或没有启用，请检查您的配置');
		}

		if (defined('GD_BUNDLED'))
		{
			Image_GD::$_bundled = GD_BUNDLED;
		}
		else
		{
			$info = gd_info();

			Image_GD::$_bundled = (bool) preg_match('/\bbundled\b/i', $info['GD Version']);
		}

		if (defined('GD_VERSION'))
		{
			$version = GD_VERSION;
		}
		else
		{
			$info = gd_info();

			preg_match('/\d+\.\d+(?:\.\d+)?/', $info['GD Version'], $matches);

			$version = $matches[0];
		}

		if ( ! version_compare($version, '2.0.1', '>='))
		{
			throw new Core_Exception('Image_GD需要GD版本：需要或更高版本',
				array('required' => '2.0.1', ':version' => $version));
		}

		return Image_GD::$_checked = TRUE;
	}

	protected $_image;

	protected $_create_function;

	public function __construct($file)
	{
		if ( ! Image_GD::$_checked)
		{
			Image_GD::check();
		}

		parent::__construct($file);

		switch ($this->type)
		{
			case IMAGETYPE_JPEG:
				$create = 'imagecreatefromjpeg';
			break;
			case IMAGETYPE_GIF:
				$create = 'imagecreatefromgif';
			break;
			case IMAGETYPE_PNG:
				$create = 'imagecreatefrompng';
			break;
		}

		if ( ! isset($create) OR ! function_exists($create))
		{
			throw new Core_Exception('安装的GD不支持 :type images',
				array(':type' => image_type_to_extension($this->type, FALSE)));
		}

		$this->_create_function = $create;

		$this->_image = $this->file;
	}

	public function __destruct()
	{
		if (is_resource($this->_image))
		{
			imagedestroy($this->_image);
		}
	}

	protected function _load_image()
	{
		if ( ! is_resource($this->_image))
		{
			$create = $this->_create_function;

			$this->_image = $create($this->file);

			imagesavealpha($this->_image, TRUE);
		}
	}

	protected function _do_resize($width, $height)
	{
		$pre_width = $this->width;
		$pre_height = $this->height;

		$this->_load_image();

		if ($width > ($this->width / 2) AND $height > ($this->height / 2))
		{
			$reduction_width  = round($width  * 1.1);
			$reduction_height = round($height * 1.1);

			while ($pre_width / 2 > $reduction_width AND $pre_height / 2 > $reduction_height)
			{
				$pre_width /= 2;
				$pre_height /= 2;
			}

			$image = $this->_create($pre_width, $pre_height);

			if (imagecopyresized($image, $this->_image, 0, 0, 0, 0, $pre_width, $pre_height, $this->width, $this->height))
			{
				imagedestroy($this->_image);
				$this->_image = $image;
			}
		}

		$image = $this->_create($width, $height);

		if (imagecopyresampled($image, $this->_image, 0, 0, 0, 0, $width, $height, $pre_width, $pre_height))
		{
			imagedestroy($this->_image);
			$this->_image = $image;

			$this->width  = imagesx($image);
			$this->height = imagesy($image);
		}
	}

	protected function _do_crop($width, $height, $offset_x, $offset_y)
	{
		$image = $this->_create($width, $height);

		$this->_load_image();

		if (imagecopyresampled($image, $this->_image, 0, 0, $offset_x, $offset_y, $width, $height, $width, $height))
		{
			imagedestroy($this->_image);
			$this->_image = $image;

			$this->width  = imagesx($image);
			$this->height = imagesy($image);
		}
	}

	protected function _do_rotate($degrees)
	{
		if ( ! Image_GD::$_bundled)
		{
			throw new Core_Exception('这种方法需要的功能，这是只适用于安装的GD',
				array(':function' => 'imagerotate'));
		}

		$this->_load_image();

		$transparent = imagecolorallocatealpha($this->_image, 0, 0, 0, 127);

		$image = imagerotate($this->_image, 360 - $degrees, $transparent, 1);

		imagesavealpha($image, TRUE);

		$width  = imagesx($image);
		$height = imagesy($image);

		if (imagecopymerge($this->_image, $image, 0, 0, 0, 0, $width, $height, 100))
		{
			imagedestroy($this->_image);
			$this->_image = $image;

			$this->width  = $width;
			$this->height = $height;
		}
	}

	protected function _do_flip($direction)
	{
		$flipped = $this->_create($this->width, $this->height);

		$this->_load_image();

		if ($direction === Image::HORIZONTAL)
		{
			for ($x = 0; $x < $this->width; $x++)
			{
				imagecopy($flipped, $this->_image, $x, 0, $this->width - $x - 1, 0, 1, $this->height);
			}
		}
		else
		{
			for ($y = 0; $y < $this->height; $y++)
			{
				imagecopy($flipped, $this->_image, 0, $y, 0, $this->height - $y - 1, $this->width, 1);
			}
		}

		imagedestroy($this->_image);
		$this->_image = $flipped;

		$this->width  = imagesx($flipped);
		$this->height = imagesy($flipped);
	}

	protected function _do_sharpen($amount)
	{
		if ( ! Image_GD::$_bundled)
		{
			throw new Core_Exception('这种方法需要的功能，这是只适用于安装的GD',
				array(':function' => 'imageconvolution'));
		}

		$this->_load_image();

		$amount = round(abs(-18 + ($amount * 0.08)), 2);

		$matrix = array
		(
			array(-1,   -1,    -1),
			array(-1, $amount, -1),
			array(-1,   -1,    -1),
		);

		if (imageconvolution($this->_image, $matrix, $amount - 8, 0))
		{
			$this->width  = imagesx($this->_image);
			$this->height = imagesy($this->_image);
		}
	}

	protected function _do_reflection($height, $opacity, $fade_in)
	{
		if ( ! Image_GD::$_bundled)
		{
			throw new Core_Exception('这种方法需要的功能，这是只适用于安装的GD',
				array(':function' => 'imagefilter'));
		}

		$this->_load_image();

		$opacity = round(abs(($opacity * 127 / 100) - 127));

		if ($opacity < 127)
		{
			$stepping = (127 - $opacity) / $height;
		}
		else
		{
			$stepping = 127 / $height;
		}

		$reflection = $this->_create($this->width, $this->height + $height);

		imagecopy($reflection, $this->_image, 0, 0, 0, 0, $this->width, $this->height);

		for ($offset = 0; $height >= $offset; $offset++)
		{
			$src_y = $this->height - $offset - 1;

			$dst_y = $this->height + $offset;

			if ($fade_in === TRUE)
			{
				$dst_opacity = round($opacity + ($stepping * ($height - $offset)));
			}
			else
			{
				$dst_opacity = round($opacity + ($stepping * $offset));
			}

			$line = $this->_create($this->width, 1);

			imagecopy($line, $this->_image, 0, 0, 0, $src_y, $this->width, 1);

			imagefilter($line, IMG_FILTER_COLORIZE, 0, 0, 0, $dst_opacity);

			imagecopy($reflection, $line, 0, $dst_y, 0, 0, $this->width, 1);
		}

		imagedestroy($this->_image);
		$this->_image = $reflection;

		$this->width  = imagesx($reflection);
		$this->height = imagesy($reflection);
	}

	protected function _do_watermark(Image $watermark, $offset_x, $offset_y, $opacity)
	{
		if ( ! Image_GD::$_bundled)
		{
			throw new Core_Exception('这种方法需要的功能，这是只适用于安装的GD',
				array(':function' => 'imagelayereffect'));
		}

		$this->_load_image();

		$overlay = imagecreatefromstring($watermark->render());

		imagesavealpha($overlay, TRUE);

		$width  = imagesx($overlay);
		$height = imagesy($overlay);

		if ($opacity < 100)
		{
			$opacity = round(abs(($opacity * 127 / 100) - 127));

			$color = imagecolorallocatealpha($overlay, 127, 127, 127, $opacity);

			imagelayereffect($overlay, IMG_EFFECT_OVERLAY);

			imagefilledrectangle($overlay, 0, 0, $width, $height, $color);
		}

		imagealphablending($this->_image, TRUE);

		if (imagecopy($this->_image, $overlay, $offset_x, $offset_y, 0, 0, $width, $height))
		{
			imagedestroy($overlay);
		}
	}

	protected function _do_background($r, $g, $b, $opacity)
	{
		$this->_load_image();

		$opacity = round(abs(($opacity * 127 / 100) - 127));

		$background = $this->_create($this->width, $this->height);

		$color = imagecolorallocatealpha($background, $r, $g, $b, $opacity);

		imagefilledrectangle($background, 0, 0, $this->width, $this->height, $color);

		imagealphablending($background, TRUE);

		if (imagecopy($background, $this->_image, 0, 0, 0, 0, $this->width, $this->height))
		{
			imagedestroy($this->_image);
			$this->_image = $background;
		}
	}

	protected function _do_save($file, $quality)
	{
		$this->_load_image();

		$extension = pathinfo($file, PATHINFO_EXTENSION);

		list($save, $type) = $this->_save_function($extension, $quality);

		$status = isset($quality) ? $save($this->_image, $file, $quality) : $save($this->_image, $file);

		if ($status === TRUE AND $type !== $this->type)
		{
			$this->type = $type;
			$this->mime = image_type_to_mime_type($type);
		}

		return TRUE;
	}

	protected function _do_render($type, $quality)
	{
		$this->_load_image();

		list($save, $type) = $this->_save_function($type, $quality);

		ob_start();

		$status = isset($quality) ? $save($this->_image, NULL, $quality) : $save($this->_image, NULL);

		if ($status === TRUE AND $type !== $this->type)
		{
			$this->type = $type;
			$this->mime = image_type_to_mime_type($type);
		}

		return ob_get_clean();
	}

	protected function _save_function($extension, & $quality)
	{
		switch (strtolower($extension))
		{
			case 'jpg':
			case 'jpeg':
				$save = 'imagejpeg';
				$type = IMAGETYPE_JPEG;
			break;
			case 'gif':
				$save = 'imagegif';
				$type = IMAGETYPE_GIF;

				$quality = NULL;
			break;
			case 'png':
				$save = 'imagepng';
				$type = IMAGETYPE_PNG;

				$quality = 9;
			break;
			default:
				throw new Core_Exception('安装的GD不支持 :type images',
					array(':type' => $extension));
			break;
		}

		return array($save, $type);
	}

	protected function _create($width, $height)
	{
		$image = imagecreatetruecolor($width, $height);

		imagealphablending($image, FALSE);

		imagesavealpha($image, TRUE);

		return $image;
	}

}
